/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { BlockCipher } from './AES/lib/BlockCipher';
import { DES } from './DES';
import { BufferedBlockAlgorithmConfig } from './BufferedBlockAlgorithmConfig';
import { WordArray } from './lib-WordArray';

export class TripleDES extends BlockCipher {
  keySize = 192 / 32
  ivSize = 64 / 32
  blockSize = 64 / 32
  _des1
  _des2
  _des3

  public constructor(xformMode: number, key: WordArray, cfg?: BufferedBlockAlgorithmConfig) {
    super(xformMode, key, cfg);
    this._doReset()
  }

  _doReset() {
    // Shortcuts
    var key = this._key;
    var keyWords = key.words;
    // Make sure the key length is valid (64, 128 or >= 192 bit)
    if (keyWords.length !== 2 && keyWords.length !== 4 && keyWords.length < 6) {
      throw new Error('Invalid key length - 3DES requires the key length to be 64, 128, 192 or >192.');
    }

    // Extend the key according to the keying options defined in 3DES standard
    var key1 = keyWords.slice(0, 2);
    var key2 = keyWords.length < 4 ? keyWords.slice(0, 2) : keyWords.slice(2, 4);
    var key3 = keyWords.length < 6 ? keyWords.slice(0, 2) : keyWords.slice(4, 6);

    // Create DES instances
    this._des1 = DES.createEncryptor(WordArray.create(key1));
    this._des2 = DES.createEncryptor(WordArray.create(key2));
    this._des3 = DES.createEncryptor(WordArray.create(key3));
  }

  encryptBlock(M, offset) {
    this._des1.encryptBlock(M, offset);
    this._des2.decryptBlock(M, offset);
    this._des3.encryptBlock(M, offset);
  }

  decryptBlock(M, offset) {
    this._des3.decryptBlock(M, offset);
    this._des2.encryptBlock(M, offset);
    this._des1.decryptBlock(M, offset);
  }
}